﻿using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using NCrontab;
using System;
using System.Collections.Concurrent;
using System.Linq;

using System.Threading;
using System.Threading.Tasks;
using Lynn.Infastructure.Utils;

namespace Lynn.Infastructure.AutoTask
{
    public abstract class TaskBackgroundService : BackgroundService
    {
        internal TaskOptions Option { get; set; }
        ConcurrentQueue<DateTime> _queue = new ConcurrentQueue<DateTime>();
        internal IServiceProvider Services { get; set; }
        //bool _first = true;
        int _times = 0;
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            stoppingToken.Register(() => {
                LogerHelper.Info($"{GetType().FullName}=>任务结束,执行{_times}次");
            });
            var delay = (int)NextTime().TotalMilliseconds;
            await Task.Delay(delay);
            while (!stoppingToken.IsCancellationRequested)
            {
                this._times++;
                await Task.Run(async () =>
                {
                    await DoWork();
                    GC.Collect();
                });
                var count = Option.Times - _times;
                if (Option.Times > 0 && count <= 0)
                {
                    stoppingToken.ThrowIfCancellationRequested();
                    await base.StopAsync(stoppingToken);
                    continue;
                }
                delay = (int)NextTime().TotalMilliseconds;
                await Task.Delay(delay);
            }
        }
        async Task DoWork()
        {
            LogerHelper.Info($"{GetType().FullName}=>任务开始执行:{_times}次");
            try
            {

                using (var scope = Services.CreateScope())
                {
                    await DoWork(scope.ServiceProvider);
                }
                LogerHelper.Info($"{GetType().FullName}=>任务执行成功:{_times}次");
            }
            catch (Exception ex)
            {
                LogerHelper.Error(GetType().FullName, ex);
            }
        }
        /// <summary>
        /// 获取下次执行
        /// </summary>
        /// <returns></returns>
        TimeSpan NextTime()
        {
            DateTime next;
            if (!_queue.TryDequeue(out next))
            {
                FindTimesByOneYear();
                _queue.TryDequeue(out next);
            }
            var start = DateTime.Now;
            var mseconds = (next - start).TotalMilliseconds;
            if (mseconds < 0)
            {
                return Option.Sliding ? TimeSpan.FromMilliseconds(0) : NextTime();
            }
            return TimeSpan.FromMilliseconds(mseconds);
        }
        /// <summary>
        /// 获取一年的运行规则
        /// </summary>
        void FindTimesByOneYear()
        {
            var opt = new CrontabSchedule.ParseOptions();
            opt.IncludingSeconds = true;
            var cron = CrontabSchedule.TryParse(Option.Cron, opt);
            if (cron == null)
            {
                Dispose();
                return;
            }
            var start = DateTime.Now;
            var end = start.AddMonths(1);
            var list = cron.GetNextOccurrences(start, end).ToList();
            if (list == null || list.Count == 0)
            {
                Dispose();
                return;
            }
            var count = Option.Times - _times;
            if (Option.Times>0) {


                if (count > 0)
                {
                    list = list.Take(count).ToList();
                }
                else {
                    list.Clear();
                }
            }
            list.ForEach(x => _queue.Enqueue(x));
        }
        protected abstract Task DoWork(IServiceProvider provider);
        public override void Dispose()
        {
            base.Dispose();
        }
    }
}
